// Copyright (c) 2012-2013, yuno. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
package sugarmintcandy.widgetwindow;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.HashMap;

import android.annotation.SuppressLint;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.AdapterView;

@SuppressLint("UseSparseArrays")
public class MyAppWidgetHost extends AppWidgetHost {
    private HashMap<Integer, MyAppWidgetHostView> mActiveViews;
    private HashMap<Integer, WeakReference<MyAppWidgetHostView>> mCachedViews;
    private int mHostId;

    public MyAppWidgetHost(Context context, int hostId) {
	super(context, hostId);
	mActiveViews = new HashMap<Integer, MyAppWidgetHostView>();
	mCachedViews = new HashMap<Integer, WeakReference<MyAppWidgetHostView>>();
	mHostId = hostId;
    }

    public void release() {
	if (ApiHelper.SDK_INT >= 11) {
	    for (WeakReference<MyAppWidgetHostView> ref : mCachedViews.values()) {
		View view = ref.get();
		if (view != null) {
		    releaseWorkerThreads(view);
		}
	    }
	}
	mActiveViews.clear();
	mCachedViews.clear();
    }

    private static void releaseWorkerThreads(View view) {
	if (view instanceof AdapterView) {
	    Adapter adapter = ((AdapterView<?>) view).getAdapter();
	    if (adapter != null) {
		Class<? extends Adapter> clazz = adapter.getClass();
		if ("android.widget.RemoteViewsAdapter".equals(clazz.getName())) {
		    try {
			Field field = clazz.getDeclaredField("mWorkerThread");
			field.setAccessible(true);
			Object value = field.get(adapter);
			if (value instanceof HandlerThread) {
			    HandlerThread thread = (HandlerThread) value;
			    final Looper looper = thread.getLooper();
			    if (looper != null) {
				Handler handler = new Handler(looper);
				handler.postAtFrontOfQueue(new Runnable() {
				    @Override
				    public void run() {
					looper.quit();
				    }
				});
			    }
			}
		    } catch (Throwable ex) {
			Debug.warn(ex);
		    }
		}
	    }
	}
	if (view instanceof ViewGroup) {
	    ViewGroup group = (ViewGroup) view;
	    int n = group.getChildCount();
	    for (int i = 0; i < n; i++) {
		releaseWorkerThreads(group.getChildAt(i));
	    }
	}
    }

    public int getHostId() {
	return mHostId;
    }

    public MyAppWidgetHostView getView(Context context, int appWidgetId,
	    AppWidgetProviderInfo providerInfo) {
	MyAppWidgetHostView view = mActiveViews.get(appWidgetId);
	if (view != null) {
	    return view;
	}
	WeakReference<MyAppWidgetHostView> ref = mCachedViews.get(appWidgetId);
	if (ref != null) {
	    view = ref.get();
	}
	if (view == null) {
	    view = (MyAppWidgetHostView) createView(context, appWidgetId,
		    providerInfo);
	    ref = new WeakReference<MyAppWidgetHostView>(view);
	    mCachedViews.put(appWidgetId, ref);
	}
	mActiveViews.put(appWidgetId, view);
	return view;
    }

    public void releaseView(MyAppWidgetHostView view) {
	int id = view.getAppWidgetId();
	if (view == mActiveViews.get(id)) {
	    mActiveViews.remove(id);
	}
    }

    public boolean isEmpty() {
	return mActiveViews.isEmpty();
    }

    @Override
    protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
	    AppWidgetProviderInfo providerInfo) {
	if (providerInfo != null) {
	    Intent intent = new Intent();
	    intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
	    intent.setComponent(providerInfo.provider);
	    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,
		    new int[] { appWidgetId });
	    context.sendBroadcast(intent);
	}
	return new MyAppWidgetHostView(context, mHostId);
    }
}
